package org.gm.service.impl;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.gm.domain.dto.MinerDTO;
import org.gm.domain.vo.GethInfoVO;
import org.gm.handler.FileLoaderHandler;
import org.gm.runnable.MinerRunnable;
import org.gm.service.GethService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.admin.Admin;
import org.web3j.protocol.core.methods.response.EthMining;
import org.web3j.protocol.geth.Geth;
import org.web3j.protocol.http.HttpService;

import javax.annotation.Resource;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;

import static org.gm.config.SchedulerConfig.minerRunnableMap;

@Slf4j
@Service
public class GethServiceImpl implements GethService {
    @Value("${file.path}")
    private String filePath;

    @Resource
    private FileLoaderHandler fileLoaderHandler;

    @Resource
    private ExecutorService executorService;

    @Override
    public List<GethInfoVO> info() throws IOException {
        List<String> ips = fileLoaderHandler.fileLoad(filePath);
        List<GethInfoVO> gethInfoVOS = new ArrayList<>();

        for (String ip : ips) {
            int index = ip.indexOf(":");

            GethInfoVO gethInfoVO = new GethInfoVO();

            gethInfoVO.setIp(ip);
            gethInfoVO.setAlive(isAlive(ip.substring(0, index), Integer.valueOf(ip.substring(index + 1))));

            if (!gethInfoVO.getAlive()) {
                gethInfoVOS.add(gethInfoVO);
                continue;
            }

            Web3j web3j = web3j(ip);
            EthMining ethMining = web3j.ethMining().send();
            gethInfoVO.setMiner(ethMining.isMining() ||
                    minerRunnableMap.get(ip) != null);
            gethInfoVOS.add(gethInfoVO);
        }

        return gethInfoVOS;
    }

    @Override
    public void openMiner(MinerDTO minerDTO) {
        MinerRunnable runnable = new MinerRunnable(geth(minerDTO.getUrl()), admin(minerDTO.getUrl()));
        executorService.execute(runnable);
        minerRunnableMap.put(minerDTO.getUrl(), runnable);
    }

    @Override
    public void closeMiner(MinerDTO minerDTO) {
        minerRunnableMap.get(minerDTO.getUrl()).close();
        minerRunnableMap.remove(minerDTO.getUrl());
    }

    private boolean isAlive(String ip, Integer port) {
        try {
            Socket socket = new Socket();
            socket.connect(new InetSocketAddress(ip, port));

            return true;
        } catch (Exception e) {
            e.printStackTrace();

            return false;
        }
    }

    private Web3j web3j(String url) {
        url = "http://" + url;
        return Web3j.build(new HttpService(url));
    }

    private Admin admin(String url) {
        url = "http://" + url;
        return Admin.build(new HttpService(url));
    }

    private Geth geth(String url) {
        url = "http://" + url;
        return Geth.build(new HttpService(url));
    }
}
